﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Jink.Core.Json.Values;
using XenoGears.Functional;
using XenoGears.Assertions;
using System.Linq;

namespace Jink.Core.Json
{
    public static class JsonParser
    {
        // todo. implement this using ANTLR
        // to be honest, the entire Json namespace should be moved to Relinq
        public static JsonValue ParseJson(this String src)
        {
            var s = (src ?? String.Empty).Trim();
            if (s.IsNullOrEmpty() || s == "null")
            {
                return null;
            }
            else
            {
                if (s[0] == '{')
                {
                    var match = Regex.Match(s, @"^\{(?<contents>.*)\}$").AssertThat(m => m.Success);
                    var parts = match.Result("${contents}").DisassembleJsonObject();
                    return new JsonObject(parts.Select(part =>
                    {
                        var kvpMatch = Regex.Match(part, @"^(?<key>.*?):(?<value>.*)$");
                        var key = kvpMatch.Result("${key}").Trim();
                        var value = kvpMatch.Result("${value}").Trim();
                        return Tuple.Create(key, value.ParseJson());
                    }));
                }
                else if (s[0] == '[')
                {
                    var match = Regex.Match(s, @"^\[(?<contents>.*)\]$").AssertThat(m => m.Success);
                    var parts = match.Result("${contents}").DisassembleJsonObject();
                    return new JsonArray(parts.Select(part => part.ParseJson()));
                }
                else
                {
                    return new JsonExpression(s);
                }
            }
        }

        // Splits JSON lists and object definitions into structural parts.
        // String.Split method won't work for lists of object.
        private static IEnumerable<String> DisassembleJsonObject(this String s)
        {
            var brackBalance = 0;
            var braceBalance = 0;
            var buffer = new StringBuilder();

            foreach (var c in s)
            {
                switch (c)
                {
                    case '[':
                        ++brackBalance;
                        break;
                    case ']':
                        --brackBalance;
                        break;
                    case '{':
                        ++braceBalance;
                        break;
                    case '}':
                        --braceBalance;
                        break;
                    default:
                        if (c == ',' && brackBalance == 0 && braceBalance == 0)
                        {
                            yield return buffer.ToString().Trim();
                            buffer = new StringBuilder();
                            continue;
                        }
                        break;
                }

                buffer.Append(c);
            }

            var lastElement = buffer.ToString().Trim();
            if (!String.IsNullOrEmpty(lastElement))
            {
                yield return lastElement;
            }
        }
    }
}
